import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

import javax.swing.JOptionPane;

public class JClipMon {
	private static Logger log;
	private static String sep;
	private static String self;
	private static String appHome;
    private static int silentPeriod = 0;
    private static ArrayList<ClipboardHandler> chs;
    public static void startTimer(int interval) {
        final Timer t = new Timer();
        t.schedule(new TimerTask() {
            @Override
            public void run() {
            	boolean trigger = false;
                String text = chs.get(0).getText();
                if ((text == null) || text.isEmpty()) {
                    silentPeriod++;
                    if (silentPeriod >= 60) {
                    	silentPeriod = 0;
                    	trigger = true;
                    	log.fine("No clipboard activity in 60 seconds, triggering handlers anyway...");
                    }
                } else {
                	trigger = true;
                }
                if (trigger) {
                	for(ClipboardHandler h: chs) {
                		try {
                			if (h.processContent()) break;
                		} catch (Exception e) {
                			log.fine("Handler<" + h.getClass().getName() + "> exception: " + e.getMessage());
                		}
                	}
                }
            }
        }, 0, interval * 1000);
    }
    private static void installHandlers(String path) {
    	String handlerSubdir, handlerName;
    	chs = new ArrayList<ClipboardHandler>();
    	ArrayList<String> handlers = new ArrayList<String>(); 
        File f = new File(path);
        File[] dirs = f.listFiles();
        if (dirs != null) {
	        for (File d : dirs) {
	        	if (!d.isDirectory()) continue;
	        	handlerSubdir = d.getPath();
				File hp = new File(handlerSubdir);
				File[] files = hp.listFiles();
				if (files != null) {
					for (File cf : files) {
						String cp = cf.getPath();
						if (cp.endsWith(".class")) {
							handlers.add(cp);
							break;
						}
					}
				}
	        }
        }
        Collections.sort(handlers);
        for (String h : handlers) {
        	int p = h.lastIndexOf(sep);
        	handlerSubdir = h.substring(0, p);
            handlerName = h.substring(p + 1, h.length() - 6);
            try {
            	log.fine("Loading handler: " + h);
            	URL[] handlerUrl = {new URL("file://" + handlerSubdir)};
                URLClassLoader hLoader = new URLClassLoader(handlerUrl);
        		Class c = hLoader.loadClass(handlerName);
        		ClipboardHandler ch = (ClipboardHandler) c.newInstance();
        		ch.setLogger(log);
            	try {
            		Properties props = new Properties();
					props.load(new FileInputStream(s(handlerSubdir) + handlerName + ".properties"));
					ch.setEnvironment(props);
				} catch (IOException e) {
					ch.setEnvironment(new Properties());
					log.fine("IOException while getting properties for: " + h);
					log.fine(e.getMessage());
				}
				if (ch.initialize())
				    chs.add(ch);
				else
					log.fine("Handler initialization failed: " + h);
            } catch (MalformedURLException e) {
            	log.fine("MalformedURLException while loading: " + h);
            	log.fine(e.getMessage());
			} catch (ClassNotFoundException e) {
            	log.fine("ClassNotFoundException while loading: " + h);
            	log.fine(e.getMessage());
			} catch (InstantiationException e) {
            	log.fine("InstantiationException while loading: " + h);
            	log.fine(e.getMessage());
			} catch (IllegalAccessException e) {
            	log.fine("IllegalAccessException while loading: " + h);
            	log.fine(e.getMessage());
			}
        }
    }
    private static String s(String path) {
    	if (path == null) return "";
    	path = path.trim();
    	return (path.endsWith(sep)) ? path : path + sep;
    }
	public static void main(String[] args) {
		sep = System.getProperty("file.separator");
		self = "jclipmon";
		String logfile = "%h/." + self + "/log%g.log";
		String errmsg = "";
		FileHandler fh = null;
		try {
			fh = new FileHandler(logfile, 8192, 2, true);
		} catch (SecurityException e) {
			errmsg = "SecurityException: " + e.getMessage();
		} catch (IOException e) {
			errmsg = "IOException: " + e.getMessage();
		}
		if (!errmsg.isEmpty()) {
			errmsg += "\n\nPlease check accessibility of <" + logfile + "> then try again.\n\n"; 
			JOptionPane.showMessageDialog(null, errmsg, self, JOptionPane.ERROR_MESSAGE);
			System.exit(0);
		}
		fh.setFormatter(new SimpleFormatter());
		log = Logger.getLogger(self);
		String logLevel = System.getProperty("logging", "FINE");
		log.setLevel(Level.parse(logLevel));
		log.addHandler(fh);
		appHome = s(System.getProperty("user.home")) + "." + self + sep;
        installHandlers(appHome + "handlers");
        if (chs.size() > 0) {
        	log.info("Handlers loaded: " + chs.size());
        	for (ClipboardHandler h : chs) {
            	log.fine("Loaded handler: " + h.getClass().getName());
            }
        	startTimer(1);
        } else {
        	log.warning("No handlers found, quitting");
        }
    }
}
